package be.kuleuven.cs.mume.objects;

import java.util.ArrayList;
import java.util.List;

import javax.media.opengl.glu.GLU;

import org.jbox2d.collision.shapes.PolygonDef;
import org.jbox2d.common.Vec2;
import org.jbox2d.dynamics.Body;
import org.jbox2d.dynamics.BodyDef;
import org.jbox2d.dynamics.World;
import org.mt4j.components.visibleComponents.GeometryInfo;
import org.mt4j.input.inputProcessors.componentProcessors.lassoProcessor.IdragClusterable;
import org.mt4j.util.math.ToolsGeometry;
import org.mt4j.util.math.Vector3D;
import org.mt4j.util.math.Vertex;
import org.mt4j.util.opengl.GluTrianglulator;

import processing.core.PApplet;
import advanced.physics.physicsShapes.PhysicsPolygon;

public class PhysicsRoundedRectangle extends PhysicsPolygon implements IdragClusterable {
	
	private static boolean free = false;
	private boolean removed = false;
	private boolean reset = false;

	public boolean isReset() {
		try{
			return reset;
		}finally{
			if (!removed & reset) {
				reset = false;
			}
		}
	}



	public boolean isRemoved() {
		return removed;
	}
	
	

	public PhysicsRoundedRectangle(float xSize, float ySize, float rRadius, int seg, Vector3D position, PApplet applet, World world, float density, float friction, float restitution,
			float worldScale) {
		super(PhysicsRoundedRectangle.tesselate(xSize, ySize, rRadius, seg), position, applet, world, density, friction, restitution, worldScale);
//		super(PhysicsRoundedRectangle.getRoundRectVerts(0, 0, 0, xSize, ySize, rRadius, rRadius, seg), position, applet, world, density, friction, restitution, worldScale);
		List<Vertex[]> list = new ArrayList<Vertex[]>();
		Vertex[] vert = new Vertex[this.vertices.length+1];
		System.arraycopy(this.vertices, 0, vert, 0, this.vertices.length);
		vert[this.vertices.length] = (Vertex) this.vertices[0].getCopy();
		list.add(vert);
		this.setOutlineContours(list);
	}
	
	private static Vertex[] tesselate(float xSize, float ySize, float rRadius , int seg){
		xSize-=rRadius*2;
		ySize-=rRadius*2;
		Vertex[] vertex = new Vertex[4*seg+4];
		int j = 0;
		float x = xSize/2, y = ySize/2;
		for(int i = 0; i < 4*seg; i++){
			if(i%seg==0){
				vertex[i+j++] = new Vertex(x+(float)Math.sin(i * .5*Math.PI/seg) * rRadius, y+(float)Math.cos(i * .5*Math.PI/seg) * rRadius);
				switch (j) {
					case 1:
						y-=ySize;
						break;
					case 2:
						x-=xSize;
						break;
					case 3:
						y+=ySize;
						break;
					case 4:
						x+=xSize;
						break;
				}
			}
			vertex[i+j] = new Vertex(x+(float)Math.cos(i * .5*Math.PI/seg) * rRadius, y+(float)Math.sin(i * .5*Math.PI/seg) * rRadius);
		}
		return vertex;
	}
	@Override
	protected void bodyDefB4CreationCallback(BodyDef def) {
		super.bodyDefB4CreationCallback(def);
		if (!free) {
			def.linearDamping = 0.3f;
			//		def.linearDamping =0.5f;
			//def.isBullet = true;
			//def.angularDamping = 0.05f;
			def.allowSleep = true;
			def.fixedRotation = true;
		}
	}
	
	public void destroyBody(){
		
		if (this.body != null) {
			System.out.println("removeBody");
			world.destroyBody(this.body);
			this.body = null;
		}
	}
	
	public void reCreateBody(boolean forAll, boolean remove){
		free = forAll;
		if(removed & !remove)
			reset=true;
		removed = remove;
		Vector3D position = new Vector3D(body.getPosition().x, body.getPosition().y);
		//Vector3D position = new Vector3D(applet.getWidth()/2, applet.getHeight()/2);
		//Vector3D position= this.getCenterPointGlobal().getCopy();
		GluTrianglulator triangulator = new GluTrianglulator(applet);
		List<Vertex> physicsTris = triangulator.tesselate(vertices, GLU.GLU_TESS_WINDING_NONZERO);
		Vertex[] triangulatedBodyVerts = physicsTris.toArray(new Vertex[physicsTris.size()]);
		triangulator.deleteTess();
		//Set the triangulated vertices as the polygons (mesh's) vertices
		this.setGeometryInfo(new GeometryInfo(applet, triangulatedBodyVerts));
		this.setPositionGlobal(new Vector3D());
		this.translate(position);
		Vector3D realBodyCenter = this.getCenterPointGlobal(); //FIXME geht nur if detached from world //rename futurebodycenter?
		//Reset position
		this.translate(position.getScaled(-1));
		
		//Now get the position where the global center will be after setting the shape at the desired position
		this.setPositionGlobal(position);
		Vector3D meshCenterAtPosition = this.getCenterPointGlobal();
		
		//Compute the distance we would have to move the vertices for the body creation
		//so that the body.position(center) is at the same position as our mesh center
		Vector3D realBodyCenterToMeshCenter = meshCenterAtPosition.getSubtracted(realBodyCenter);
		//System.out.println("Diff:" +  realBodyCenterToMeshCenter);
		
		//Move the vertices so the body position is at the center of the shape 
		Vertex.translateVectorArray(triangulatedBodyVerts, realBodyCenterToMeshCenter);
		
		this.setGeometryInfo(new GeometryInfo(applet, triangulatedBodyVerts));
		
		Vertex.translateVectorArray(vertices, realBodyCenterToMeshCenter);
		
		//Create vertex structure for creation of decomposition polygon (use the translated vertices)
		float xArr[] = new float[vertices.length];
		float yArr[] = new float[vertices.length];
		for (int i = 0; i < vertices.length; i++) {
			Vertex v = vertices[i];
			xArr[i] = v.x;
			yArr[i] = v.y;
		}
		//Create a polygon too see if its simple and eventually decompose it
		org.jbox2d.util.nonconvex.Polygon myPoly = new org.jbox2d.util.nonconvex.Polygon(xArr, yArr);
		
		PolygonDef pd = new PolygonDef();
    	if (getDensity() != 0.0f){
    		pd.density 		= this.getDensity();
    		pd.friction 	= this.getFriction();
    		pd.restitution 	= this.getRestituion();
		}
		
		//Create polygon body
    	world.destroyBody(this.body);
		BodyDef dymBodyDef = new BodyDef();
		dymBodyDef.position = new Vec2(position.x , position.y );
		if(!remove)
			this.bodyDefB4CreationCallback(dymBodyDef);
		this.body = world.createBody(dymBodyDef);
		
		
		//Decompose poly and add verts to phys body if possible
    	int success = org.jbox2d.util.nonconvex.Polygon.decomposeConvexAndAddTo(myPoly, this.body, pd); 
    	
    	if (success != -1){
    		System.out.println("-> Ear clipping SUCCESSFUL -> Using triangulated and polygonized shape for b2d.");
    		this.body.setMassFromShapes();
    		this.body.setUserData(this);
	    	this.setUserData("box2d", this.body); //TODO rename userData
			//Performance hit! but prevents object from sticking to another sometimes
//			theBody.setBullet(true);
    	}else{
    		System.out.println("-> Ear clipping had an ERROR - trying again by triangulating shape for b2d with GLU-Triangulator");
//    		GluTrianglulator triangulator = new GluTrianglulator(app);
//    		List<Vertex> physicsTris = triangulator.tesselate(bodyVerts, GLU.GLU_TESS_WINDING_NONZERO);
//    		Vertex[] triangulatedBodyVerts = physicsTris.toArray(new Vertex[physicsTris.size()]);
    		//System.out.println("GLU tris created: " + triangulatedBodyVerts.length);
    		
    		//Cap the max triangles - dont use anymore triangles for the physics body..
    		int cap = 400;
    		if (triangulatedBodyVerts.length > cap){
    			//System.err.println("OVER cap! -> capping!");
    			Vertex[] tmp = new Vertex[cap];
    			System.arraycopy(triangulatedBodyVerts, 0, tmp, 0, cap);
    			triangulatedBodyVerts = tmp;
    		}
    		
    		//Create polygon body
    		world.destroyBody(this.body);
    		dymBodyDef = new BodyDef();
    		dymBodyDef.position = new Vec2(position.x, position.y);
    		if(!remove)
    			this.bodyDefB4CreationCallback(dymBodyDef);
    		this.body = world.createBody(dymBodyDef);
    		for (int i = 0; i < triangulatedBodyVerts.length/3; i++) {
    			//Create polygon definition
    			PolygonDef polyDef = new PolygonDef();
    			if (getDensity() != 0.0f){
    				polyDef.density 		= getDensity();
    				polyDef.friction 		= getFriction();
    				polyDef.restitution 	= getRestituion();
    			}
    			this.polyDefB4CreationCallback(polyDef); //FIXME TEST
    			
    			//Add triangle vertices
    			Vertex vertex1 = triangulatedBodyVerts[i*3];
    			Vertex vertex2 = triangulatedBodyVerts[i*3+1];
    			Vertex vertex3 = triangulatedBodyVerts[i*3+2];
    			polyDef.addVertex(new Vec2(vertex1.x, vertex1.y));
    			polyDef.addVertex(new Vec2(vertex2.x, vertex2.y));
    			polyDef.addVertex(new Vec2(vertex3.x, vertex3.y));
    			//Add poly to body
    			this.body.createShape(polyDef);
    		}
    		this.body.setMassFromShapes();
    		//FIXME TEST - performance hit!?
    		//theBody.setBullet(true);
    		this.body.setUserData(this);
    		this.setUserData("box2d", this.body); //TODO rename userData
    	}
	}
	private boolean selected;
	/**
	 * @see org.mt4j.input.inputProcessors.componentProcessors.lassoProcessor.IdragClusterable#isSelected()
	 */
	@Override
	public boolean isSelected() {
		return this.selected;
	}

	/**
	 * @see org.mt4j.input.inputProcessors.componentProcessors.lassoProcessor.IdragClusterable#setSelected(boolean)
	 */
	@Override
	public void setSelected(boolean selected) {
		this.selected = selected;
	}

}
